/**
 * LSDatabase.java
 *
 * @author Philip Kwok
 * @author Myongsu Choe
 */


package SSF.OS.OSPF;


import java.util.*;


// ====================== class SSF.OS.OSPF.LSDatabase ===================== //
/**
 * An OSPF Link State database.
 */ 
public class LSDatabase {

  // ......................... constants ........................... //

  public static final int ROUTER_LINK = 1;
  public static final int SUMMARY_NET = 2;
  public static final int AS_BOUNDARY = 3;
  public static final int AS_EXT      = 4;
  
  // ........................ member data .......................... //

  /** The OSPF instance with which this database is associated. */
  private sOSPF ospf;
  
  /** A list of physical links between one given router and other routers in
   *  the same network area (to which it is connected). */
  public Hashtable RouterLink = new Hashtable();
  public Hashtable SummaryNet = new Hashtable();
  public Hashtable ASboundary = new Hashtable();
  public Hashtable ASExt      = new Hashtable();

  /** A boolean variable to indicate if this OSPF Database has been modified
   *  since last operated on by Dijkstra. */
  private boolean modified = false;


  // ------------------------ constructor LSDatabase ----------------------- //
  public LSDatabase(sOSPF o) {
    ospf = o;
  }
  
  // ------------------ LSDatabase.toString(String,boolean) ---------------- //
  /**
   * Returns this OSPF link state database as a string.
   *
   * @param ind     A string to use for indenting each line of the output
   *                string.
   * @param usenhi  Whether to use NHI addressing or IP addressing.
   * @return the OSPF link state database as a string
   */
  public String toString(String ind, boolean usenhi) {
    Vector infolist = null; // all link info for a particular router
    String rtrnh = null;

    String str = ind + "- - - - - Router Links\n";
    for (Enumeration enum=RouterLink.keys(); enum.hasMoreElements();) {
      rtrnh = (String)enum.nextElement();
      infolist = (Vector)RouterLink.get(rtrnh);
      if (!infolist.isEmpty()) {
        str += ind + "router " + rtrnh + " --->\n";
        str += linksToString(infolist,ind+"   ",usenhi);
      }
    }

    //str += ind + "- - - - - Summary Networks\n";
    //for (Enumeration enum=SummaryNet.keys(); enum.hasMoreElements();) {
    //  rtrnh = (String)enum.nextElement();
    //  infolist = (Vector)SummaryNet.get(rtrnh);
    //  if (!infolist.isEmpty()) {
    //    str += ind + "router " + rtrnh + " --->\n";
    //    str += linksToString(infolist,ind+"   ",usenhi);
    //  }
    //}

    //str += ind + "- - - - - Summary AS Boundary Links\n";
    //for (Enumeration enum=ASboundary.keys(); enum.hasMoreElements();) {
    //  rtrnh = (String)enum.nextElement();
    //  infolist = (Vector)ASboundary.get(rtrnh);
    //  if (!infolist.isEmpty()) {
    //    str += ind + "router " + rtrnh + " --->\n";
    //    str += linksToString(infolist,ind+"   ",usenhi);
    //  }
    //}

    str += ind + "- - - - - AS External Links\n";
    for (Enumeration enum=ASExt.keys(); enum.hasMoreElements();) {
      rtrnh = (String)enum.nextElement();
      infolist = (Vector)ASExt.get(rtrnh);
      if (!infolist.isEmpty()) {
        str += ind + "router " + rtrnh + "  --->\n";
        str += linksToString(infolist,ind+"   ",usenhi);
      }
    }

    return str;
  }

  // ----------------- LSDatabase.toVString(String,boolean) ---------------- //
  /**
   * Returns this OSPF link state database as a string specially formatted for
   * use with a validation test.
   *
   * @param ind     A string to use for indenting each line of the output
   *                string.
   * @param usenhi  Whether to use NHI addressing or IP addressing.
   * @return the OSPF link state database as a string for use with a validation
   *         test
   */
  public String toVString(String ind, boolean usenhi) {
    Vector infolist = null; // all link info for a particular router
    String rtrnh = null;

    String str = ind + "RL  - - - - - Router Links\n";
    for (Enumeration enum=RouterLink.keys(); enum.hasMoreElements();) {
      rtrnh = (String)enum.nextElement();
      infolist = (Vector)RouterLink.get(rtrnh);
      if (!infolist.isEmpty()) {
        str += ind + "RL " + rtrnh + "  router " + rtrnh + " --->\n";
        str += linksToString(infolist,ind+"RL "+rtrnh+" .   ",usenhi);
      }
    }

    //str += ind + "SN  - - - - - Summary Networks\n";
    //for (Enumeration enum=SummaryNet.keys(); enum.hasMoreElements();) {
    //  rtrnh = (String)enum.nextElement();
    //  infolist = (Vector)SummaryNet.get(rtrnh);
    //  if (!infolist.isEmpty()) {
    //    str += ind + "SN " + rtrnh + "  router " + rtrnh + " --->\n";
    //    str += linksToString(infolist,ind+"SN "+rtrnh+" .   ",usenhi);
    //  }
    //}

    //str += ind + "SA  - - - - - Summary AS Boundary Links\n";
    //for (Enumeration enum=ASboundary.keys(); enum.hasMoreElements();) {
    //  rtrnh = (String)enum.nextElement();
    //  infolist = (Vector)ASboundary.get(rtrnh);
    //  if (!infolist.isEmpty()) {
    //    str += ind + "SA " + rtrnh + "  router " + rtrnh + " --->\n";
    //    str += linksToString(infolist,ind+"SA "+rtrnh+" .   ",usenhi);
    //  }
    //}

    str += ind + "AE  - - - - - AS External Links\n";
    for (Enumeration enum=ASExt.keys(); enum.hasMoreElements();) {
      rtrnh = (String)enum.nextElement();
      infolist = (Vector)ASExt.get(rtrnh);
      if (!infolist.isEmpty()) {
        str += ind + "AE " + rtrnh + "  router " + rtrnh + "  --->\n";
        str += linksToString(infolist,ind+"AE "+rtrnh+" .   ",usenhi);
      }
    }

    return str;
  }

  // ------------------------- LSDatabase.toString() ----------------------- //
  /**
   * Returns this OSPF link state database as a string.
   *
   * @return the OSPF link state database as a string
   */
  public String toString() {
    return toString("",false);
  }
  
  // --------------- LSDatabase.linksToString(String,boolean) -------------- //
  /**
   * Converts a table of link information to a string.
   *
   * @param ind     A string to use for indenting each line of the output
   *                string.
   * @param usenhi  Whether to use NHI addressing or IP addressing.
   * @return the table of link info as a string
   */
  public static String linksToString(Vector list, String ind, boolean usenhi) {
    String str = "";
    for (int i=0; i<list.size(); i++) {
      str += ind + ((LinkInfo)list.elementAt(i)).toString(usenhi);
      if (i != list.size()-1) {
        str += " -->";
      }
      str += "\n";
    }
    return str;
  }

  // ----------------------- LSDatabase.getStubNetworks -------------------- //
  /**
   * Returns a list of all the stub network links in this database.
   */
  public Vector getStubNetworks() {
    return getNetworks(RouterLink);
  }
  
  // ------------------------ LSDatabase.getSummaryNets -------------------- //
  /**
   * Returns a list of all the stub network links in other databases.
   */
  public Vector getSummaryNets() {
    return getNetworks(SummaryNet);
  }

  // ------------------------- LSDatabase.getASboundary -------------------- //
  /**
   * Returns a list of all the AS boundary routers in this database.
   */
  public Vector getASboundary() {
    return getNetworks(ASboundary);
  }

  // ----------------------- LSDatabase.getASExtNetworks ------------------- //
  /**
   * Returns a list of all the AS boundary routers in this database.
   */
  public Vector getASExtNetworks() {
    return getNetworks(ASExt);
  }
  
  // ------------------------- LSDatabase.getNetworks ---------------------- //
  /**
   * Builds a list of link info by going through all link info in a given
   * section of the database and selecting link info which is either TO_STUB,
   * TO_HOST, or AS_EXTERNAL.
   *
   * @param dbsection  The section of the database to be examined.
   */
  private Vector getNetworks(Hashtable dbsection) {
    Vector newinfolist = new Vector();
    Vector linkinfolist = null;
    String rtrnh = null;

    Vector dbsorted = new Vector(dbsection.size());
    for (Enumeration enum=dbsection.keys(); enum.hasMoreElements();) {
      rtrnh = (String)enum.nextElement();
      int i=-1;
      String tmpnh = null;
      do {
        i++;
        if (i < dbsorted.size()) {
          tmpnh = (String)dbsorted.elementAt(i);
        } else {
          tmpnh = null;
        }
      } while (tmpnh != null && rtrnh.compareTo(tmpnh) > 0);
      dbsorted.insertElementAt(rtrnh,i);
    }

    for (int h=0; h<dbsorted.size(); h++) {
      linkinfolist = (Vector)dbsection.get((String)dbsorted.elementAt(h));
      for (int i=0; i<linkinfolist.size(); i++) {
        LinkInfo nd = (LinkInfo)linkinfolist.elementAt(i);
        if (nd.TypeOneType == sOSPF.TO_STUB || 
            nd.TypeOneType == sOSPF.TO_HOST ||
            nd.TypeOneType == LSA.AS_EXTERNAL_LSA) {
          newinfolist.addElement(nd);
        }
      }
    }
    return newinfolist;
  }

  // -------------- LSDatabase.updateLink(int,String,LinkInfo) ------------- //
  /**
   * Each element of RouterLink keeps track of physical links between a given
   * router and other routers in the same network area.
   */
  public void updateLink(int linktype, String nhipref, LinkInfo newNode) {
    modified = true;

    Hashtable dbsection;
    switch (linktype) {
    case ROUTER_LINK:  dbsection = RouterLink;   break;
    case SUMMARY_NET:  dbsection = SummaryNet;   break;
    case AS_BOUNDARY:  dbsection = ASboundary;   break;
    case AS_EXT:       dbsection = ASExt;        break;
    default:           return;
    }
      
    // reference to the table of link info for the given router
    Vector mylist = (Vector)dbsection.get(nhipref);

    if (mylist == null) { // no link info for this router yet exists
      mylist = new Vector();
      dbsection.put(nhipref,mylist);
    }
    
    // Add the new link info node to the list of link info.  Any previously
    // existing node for the same destination is replaced.

    // Calling 'remove' with this argument may seem strange, but it will remove
    // any previously existing link info which "equals" this one, if any
    // exists.  (See LinkInfo.equals().)  Then we insert the new one.
    mylist.removeElement(newNode);
    mylist.addElement(newNode);
  }
    
  // ------------------------- LSDatabase.isModified ----------------------- //
  /**
   * Return true if this OSPF Database has been modified after last Dijkstra.
   */
  public boolean isModified() {
    return modified;
  }

  // ------------------------- LSDatabase.setModified ---------------------- //
  public void setModified(boolean mod) {
    modified = mod;
  }

} // end class LSDatabase
